<!doctype html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>控制机房网络</title>
    <script src="/static/vue-production.js"></script>
    <link rel="stylesheet" href="/static/index.css">
    <script src="/static/index.js"></script>
</head>

<body th:with="ismgr=${session.role}=='管理员'">
    <div id="app" style="width:500px;margin:auto;text-align:center">
        <div th:if="${ismgr}">
            <el-button v-for="tab in tabs" type="primary" size="mini" :key="tab.id" @click="currenttab=tab.comp">{{tab.txt}}</el-button><br/>
            <keep-alive>
                <component :is="currenttab"></component>
            </keep-alive>
            <br/>
            <el-button type="danger" size="mini" @click="initdb" style="position:absolute;top:10px;right:10px;">初始化数据库</el-button>
            <el-button type="primary" size="mini" @click="backupDb">备份数据库</el-button>
            <el-button type="primary" size="mini" @click="restoreDb">{{restoreClicked?'还原':'还原数据库'}}</el-button>
            <br>
            <input type="file" v-if="restoreClicked" id="upload" @change="filechange($event)">
        </div>
        <net th:unless="${ismgr}"></net><br/>
        <el-button type="warning" size="mini" @click="logoff" style="float:right;">安全退出</el-button>
    </div>
    <script th:inline="javascript">
        [# th:if="${ismgr}"]
        var jsonheader = new Headers({ 'content-type': 'application/json;charset=UTF-8' })
        var formheader = new Headers({'content-type':'application/x-www-form-urlencoded;charset=utf-8'})
        Vue.component('room', {
            template: `<el-table v-loading="loading" :data="rows">
            <el-table-column label="房间" width="100px">
                <template v-slot="{row}">
                    <el-input v-model="row.name" @change="change(row,'name')" @blur="row.edit=false"
                        v-if="row.edit"></el-input>
                    </el-input>
                    <span v-else @click="row.edit=true">{{row.name?row.name:"请输入"}}</span>
                </template>
            </el-table-column>
            <el-table-column label="交换机接口" width="100px">
                <template v-slot="{row}">
                    <el-select v-model="row.iface" @change="change(row,'iface')" size="mini">
                        <el-option v-for="item in ifaces" :key="item" :label="item" :value="item">
                        </el-option>
                    </el-select>
                </template>
            </el-table-column>
            <el-table-column label="接口类型" width="120px">
                <template v-slot="{row}">
                    <el-select v-model="row.itype" @change="change(row,'itype')" size="mini">
                        <el-option v-for="item in itypes" :key="item" :label="item" :value="item">
                        </el-option>
                    </el-select>
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template v-slot="{row,$index}">
                    <el-button type="primary" size="mini" @click="commitrow(row)" :disabled="row.notchg">提交</el-button>
                    <el-button type="danger" size="mini" @click="delrow(row.old.name,$index)" v-if="$index<rows.length-1">删除</el-button>
                </template>
            </el-table-column>
        </el-table>`,
            data() {
                return {
                    itypes: ['GigabitEthernet', 'Ethernet'],
                    rows: null,
                    loading: true
                }
            },
            methods: {
                initrow() {
                    return {
                        name: '',
                        itype: '',
                        iface: '',
                        edit: false,
                        notchg: true,
                        old: {
                            name: '',
                            itype: '',
                            iface: ''
                        },
                        post: {}
                    }
                },
                dualnames(row) {
                    let dual = false
                    let arr = this.rows
                    for (let i = 0; i < arr.length; i++) {
                        if (arr[i] != row) {
                            if (row.name == arr[i].name || row.name == arr[i].old.name) {
                                dual = true
                                break
                            }
                        }
                    }
                    return dual
                },
                change(row, chg) {
                    if (chg == 'name') {
                        if (this.dualnames(row)) {
                            row.notchg = true
                            alert('房间名称重复');
                            row.name = row.old.name;
                            return
                        }
                    }
                    if (row.old[chg] == row[chg]) {
                        delete row.post[chg]
                    } else {
                        row.post[chg] = row[chg];
                    }
                    if (row.name == row.old.name && row.iface == row.old.iface && row.itype == row.old.itype) {
                        row.notchg = true
                        return
                    }
                    if (row.name && row.iface && row.itype) {
                        row.notchg = false;
                    }
                },
                commitrow(row) {
                    row.notchg = true
                    if (!row.name || this.dualnames(row)) {
                        return
                    }
                    row.post.oldname = row.old.name
                    fetch('/mgr/modroom', {
                        method: 'post',
                        headers: jsonheader,
                        body: JSON.stringify(row.post)
                    }).then(res => {
                        row.post = {}
                        row.old.iface = row.iface
                        row.old.itype = row.itype
                        if (row.old.name != row.name) {
                            if (!row.old.name) {
                                this.rows.push(this.initrow())
                            }
                            row.old.name = row.name
                        }
                    }).catch(err => alert(err))

                },
                delrow(name, i) {
                    answer = confirm(`确定要删除 "${name}" 吗?`)
                    if (answer) {
                        var fd = new FormData()
                        fd.append('name', name)
                        fd.append('tb', 'cfg_room')
                        fetch('/mgr/delete', {
                            method: 'post',
                            body: fd
                        }).then(res => {
                            this.rows.splice(i, 1)
                        }).catch(err => alert(err))
                    }
                }
            },
            computed: {
                ifaces() {
                    faces = new Array(48)
                    for (let i = 0; i < 48; i++) {
                        faces[i] = '0/0/' + (i + 1)
                    }
                    return faces
                },
            },
            created() {
                this.loading = true;
                fetch('/mgr/getrooms', {
                    method: 'post'
                }).then(res => {
                    return res.json()
                }).then(res => {
                    res.forEach(row => {
                        this.$set(row, 'edit', false)
                        this.$set(row, 'notchg', true)
                        row.post = {}
                        row.old = {}
                        row.old.name = row.name
                        row.old.iface = row.iface
                        row.old.itype = row.itype
                    });
                    this.rows = res;
                    this.rows.push(this.initrow())
                }).catch(err => alert(err)).finally(f=>this.loading = false)
            }
        });
        Vue.component('args', {
            template: `<el-table :data="rows" v-loading="loading">
            <el-table-column v-for="col in columns" :prop="col.id" :key="col.id" :label="col.label" :width="col.width">
            </el-table-column>
            <el-table-column label="值" width="150px">
                <template v-slot="{row}">
                    <el-input v-model="row.val" v-if="row.edit" @change="change(row)" @blur="row.edit=false" :show-password="row.name=='SshPwd'"></el-input>
                    <span v-else @click="row.edit=true">{{row.name=='SshPwd'?'******':row.val}}</span>
                </template>
            </el-table-column>
        </el-table>`,
            data() {
                return {
                    rows: null,
                    columns: [{
                        id: 'name',
                        label: '变量',
                        width: '100px'
                    }, {
                        id: 'desc',
                        label: '描述',
                        width: 'auto'
                    }],
                    loading: true
                }
            },
            methods: {
                change(row) {
                    var fd = new FormData()
                    fd.append('name', row.name)
                    fd.append('val', row.val)
                    fetch('/mgr/modarg', {
                        method: 'post',
                        body: fd
                    }).catch(err => alert(err))
                },
                getArgs() {
                    this.loading = true;
                    fetch('/mgr/getargs', {
                        method: 'post'
                    }).then(res => {
                        return res.json()
                    }).then(data => {
                        data.forEach(e => {
                            this.$set(e, 'edit', false)
                        });
                        this.rows = data;
                    }).catch(err=>console.log(err)).finally(f=>{
                        this.loading = false
                    })
                }
            },
            created() {
                this.getArgs();
            }
        })
        Vue.component('users', {
            data() {
                return {
                    roles: ['管理员', '操作员'],
                    rows: null,
                    loading: true
                }
            },
            methods: {
                initrow() {
                    return {
                        name: '',
                        pwd: '',
                        role: '',
                        edit: false,
                        notchg: true,
                        old: {
                            name: '',
                            pwd: '',
                            role: ''
                        },
                        post: {}
                    }
                },
                dualnames(row) {
                    let dual = false
                    let arr = this.rows
                    for (let i = 0; i < arr.length; i++) {
                        if (arr[i] != row) {
                            if (row.name == arr[i].name || row.name == arr[i].old.name) {
                                dual = true
                                break
                            }
                        }
                    }
                    return dual
                },
                hasmgr(row) {
                    return new Set(this.rows.filter(e => e != row).map(e => e.old.role)).has('管理员')
                },
                initrole(row) {
                    this.rows.forEach(e => {
                        if (e != row) {
                            e.notchg = true
                            e.role = e.old.role
                            e.post = {}
                        }
                    })
                },
                change(row, chg) {
                    if (chg == 'name') {
                        if (this.dualnames(row)) {
                            row.notchg = true
                            if (row.name) alert('用户名重复或已存在')
                            else alert('用户名不允许为空')
                            row.name = row.old.name
                            return
                        }
                    } else if (chg == 'role' && !this.hasmgr(row)) {
                        row.notchg = true
                        alert('至少需要一个管理员')
                        row.role = row.old.role
                        return
                    } else {
                        if (!row.pwd) {
                            row.notchg = true
                            alert('密码不允许为空')
                            row.pwd = row.old.pwd
                            return
                        }
                    }
                    if (row.old[chg] == row[chg]) {
                        delete row.post[chg]
                    } else {
                        row.post[chg] = row[chg];
                    }
                    if (row.name == row.old.name && row.pwd == row.old.pwd && row.role == row.old.role) {
                        row.notchg = true
                        return
                    }
                    if (row.name && row.pwd && row.role) {
                        row.notchg = false;
                    }
                },
                commitrow(row) {
                    row.notchg = true
                    if (!row.name || !row.pwd || this.dualnames(row)) {
                        return
                    }
                    row.post.oldname = row.old.name
                    fetch('/mgr/moduser', {
                        method: 'post',
                        headers: jsonheader,
                        body: JSON.stringify(row.post)
                    }).then(res => {
                        if (!res.ok) return;
                        row.post = {}
                        row.old.pwd = row.pwd
                        row.old.role = row.role
                        if (row.old.name != row.name) {
                            if (!row.old.name) {
                                this.rows.push(this.initrow())
                            }
                            row.old.name = row.name
                        }
                        this.initrole(row)
                    }).catch(err => alert(err))
                },
                delrow(row, i) {
                    if (!this.hasmgr(row)) return alert('至少需要一个管理员')
                    answer = confirm(`确定要删除 "${row.old.name}" 吗?`)
                    if (answer) {
                        var fd = new FormData()
                        fd.append('name', row.old.name)
                        fd.append('tb', 'cfg_users')
                        fetch('/mgr/delete', {
                            method: 'post',
                            body: fd
                        }).then(res => {
                            if (res.ok) this.rows.splice(i, 1)
                        }).catch(err => alert(err))
                    }
                },
                getUsers() {
                    this.loading = true;
                    fetch('/mgr/getusers', {
                        method: 'post'
                    }).then(res => {
                        return res.json()
                    }).then(res => {
                        res.forEach(e => {
                            this.$set(e, 'edit', false)
                            this.$set(e, 'notchg', true)
                            e.post = {}
                            e.old = {}
                            e.old.name = e.name
                            e.old.pwd = e.pwd
                            e.old.role = e.role
                        });
                        this.rows = res;
                        this.rows.push(this.initrow())
                    }).catch(err => alert(err)).finally(f=>{this.loading = false})
                }
            },
            created() {
                this.getUsers()
            },
            template: `<el-table :data="rows" v-loading="loading">
            <el-table-column label="用户名" width="100px">
                <template v-slot="{row}">
                    <el-input v-model="row.name" v-if="row.edit" @change="change(row,'name')" @blur="row.edit=false"></el-input>
                    <span v-else @click="row.edit=true">{{row.name?row.name:"请输入"}}</span>
                </template>
            </el-table-column>
            <el-table-column label="密码" width="80px">
                <template v-slot="{row}">
                    <el-input show-password v-model="row.pwd" v-if="row.edit" @change="change(row,'pwd')" @blur="row.edit=false;"></el-input>
                    <span v-else @click="row.edit=true">{{row.pwd?'******':"请输入"}}</span>
                </template>
            </el-table-column>
            <el-table-column label="角色">
                <template v-slot="{row}">
                    <el-select v-model="row.role" @change="change(row,'role')" size="mini">
                        <el-option v-for="item in roles" :key="item" :label="item" :value="item" size="mini"></el-option>
                    </el-select>
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template v-slot="{row,$index}">
                        <el-button type="primary" size="mini" @click="commitrow(row)" :disabled="row.notchg">提交</el-button>
                        <el-button type="danger" size="mini" @click="delrow(row,$index)" v-if="$index<rows.length-1">删除</el-button>
                </template>
            </el-table-column>
        </el-table>`
        })
        Vue.component('log', {
            data() {
                return {
                    logs: [],
                    cols: [{ id: 'time', label: '时间', width: '150px' }, { id: 'action', label: '日志', width: 'auto' }],
                    cuNo: 0
                }
            }
            , methods: {
                getPage(num) {
                    var fd = new FormData()
                    fd.append('pgNo', num)
                    fetch('/mgr/logs', { method: 'post', body: fd }).then(res => res.json())
                        .then(jsonObj => {
                            this.cuNo = num
                            this.logs = jsonObj
                        })
                },
                clean() {
                    if (confirm('确定要清除日志吗?'))
                        fetch('/mgr/cleanlog', { method: 'post' }).then(res=>{
                            this.getPage(0)
                        }).catch(err => console.log(err))
                }
            }
            , created() {
                this.getPage(0)
            },
            template: `<div>
        <el-table :data="logs">
            <el-table-column label="序号" width="60px">
                <template v-slot="{$index}">
                    <span>{{$index+1+cuNo*100}}</span>
                </template>
            </el-table-column>
            <el-table-column v-for="col in cols"
                :prop="col.id"
                :key="col.id"
                :label="col.label" :width="col.width">
            </el-table-column>
        </el-table>
        
        <el-button type="primary" size="default" @click="getPage(0)">刷新</el-button>
        <el-button type="primary" size="default" @click="getPage(cuNo-1)" :disabled="cuNo===0">上一页</el-button>
        <el-button type="primary" size="default" @click="getPage(cuNo+1)" :disabled="logs.length < 100">下一页</el-button>
        <el-button type="primary" size="default" @click="clean()">清除一周前记录</el-button>
    </div>`
        })
        [/]
        Vue.component('net', {
            data() {
                return {
                    rows: null,
                    loading: true,
                    delay: 0,
                    duration: 0,
                    taskbtn: null
                }
            },
            methods: {
                reqState() {
                    this.loading = true
                    fetch('/getstate', {method: 'post'}).then(res =>{
                        if (res.redirected) {
                            location.pathname = '/'
                        } else {
                            return res.json()
                        }
                    }).then(st => {
                        if(!st){
                            return alert('获取状态失败，尝试"重连接交换机"或联系管理员')
                        }
                        this.rows = []
                        Object.keys(st).sort().forEach(e => {
                            this.rows.push({ room: e, state: st[e] })
                        })
                    }).finally(f=>{
                        this.loading = false
                    })
                },
                change(row, index) {
                    this.loading = true
                    var fd = new FormData()
                    fd.append('room', row.room)
                    fd.append('state', row.state)
                    fd.append('index', index)
                    fd.append('delay',this.delay)
                    fd.append('duration',this.duration)
                    fetch('/togglestate', {
                        method: 'post',
                        body: fd
                    }).then(res => {
                        if (res.redirected) {
                            location.pathname = '/'
                        }else {
                            row.state = this.delay?!row.state:row.state
                            return res.json()
                        }
                    }).then(js => {
                        if (!js) return alert('开关异常，尝试"重连接交换机"或联系管理员')
                        if(this.delay > 0 || this.duration > 0) this.taskbtn.click()
                    }).catch(err => {
                        row.state = !row.state
                        alert(err)
                    }).finally(f=>{
                        this.loading = false
                        this.delay = 0
                        this.duration = 0
                    })
                },
                checktm(tm){
                    if(!Number.isInteger(parseFloat(this[tm]))){
                        alert('必须填入整数')
                        this[tm] = 0
                    }else if(this[tm] < 0){
                        alert('时间不能为负数')
                        this[tm] = 0
                    }else if(this[tm] > 600){
                        alert('时间不能大于600分钟')
                        this[tm]=0
                    }
                },
                toggleAll(open){
                    this.loading = true
                    var fd = new FormData()
                    fd.append('open',open)
                    fd.append('delay',this.delay)
                    fd.append('duration',this.duration)
                    fetch('/toggleAll',{method:'post',body:fd}).then(res=>{
                        if (res.redirected) {
                            location.pathname = '/'
                        } 
                        if(!this.delay)
                        this.rows.forEach(row=>row.state=open)
                        return res.json()
                    }).then(js => {
                        if (!js) alert('开关异常，尝试"重连接交换机"或联系管理员')
                        if(this.delay > 0 || this.duration >0) this.taskbtn.click()
                    }).catch(err=>alert(err)).finally(f=>{
                        this.loading = false
                        this.delay = 0
                        this.duration = 0
                    })
                },
                reConn(){
                    this.loading = true;
                    fetch('reConn',{method:'post'}).then(res=>{return res.json()}).then(r=>{
                        if(!r) alert('重连接交换机失败，请联系管理员')
                    }).catch(err=>alert(err)).finally(f=>this.loading = false)
                }
            },
            mounted() {
                this.taskbtn = document.getElementById('task')
                this.reqState()
                ws.addEventListener('message', e => {
                    this.loading = true
                    jstr = JSON.parse(e.data)
                    if(jstr.index != undefined) {
                        this.rows[jstr.index].state = jstr.state
                    }else{
                        this.rows = []
                        Object.keys(jstr).sort().forEach(l => {
                            this.rows.push({ room: l, state: jstr[l] })
                        })
                    }
                    this.loading = false
                })
            },
            template: `<div v-loading="loading">
            <el-table :data="rows">
                <el-table-column prop="room" label="机房">
                </el-table-column>
                <el-table-column label="状态">
                    <template v-slot="{row,$index}">
                        <span>{{row.state?'允许':'禁止'}}</span> &nbsp;&nbsp;&nbsp;
                        <el-switch v-model="row.state" active-color="#13ce66" inactive-color="#ff4949" @change="change(row,$index)" :disabled="loading">
                        </el-switch>
                    </template>
                </el-table-column>
            </el-table>
            <br/>
            <div style="text-align:left;">
                <div>开启或关闭<input type="number" min="0" max="600" v-model="duration" @change="checktm('duration')"/>分钟，默认0无限制</div>
                <div>延迟<input type="number" min="0" max="600" v-model="delay" @change="checktm('delay')"/>分钟后再执行，默认0即时生效</div>
                <span>时间设置仅对下一次开关操作生效，必须在每次操作前设置</span>
            </div>
                <el-button type="primary" size="mini" @click="reqState">刷新状态</el-button>
                <el-button type="primary" size="mini" @click="toggleAll(true)">全部开启</el-button>
                <el-button type="primary" size="mini" @click="toggleAll(false)">全部关闭</el-button>
                <el-button type="primary" size="mini" @click="reConn">重连接交换机</el-button>
                <br/><br/>
                <tasklist/>
        </div>`
        })
        Vue.component('tasklist',{
            data(){
                return {
                    loading: false,
                    rows: [],
                    columns: [{id:'tm',label:'执行时间',width:'150px'},
                    {id:'schedule',label:'任务描述',width:'auto'}]
                }
            },
            methods: {
                delTask(row,i){
                    this.loading = true
                    let fm = new FormData();
                    fm.append('tm',row.stamp);
                    fetch('/delTask',{body:fm,method:'post'}).then(res=>{
                        this.rows.splice(i,1)
                    }).catch(err=>alert(err)).finally(()=>this.loading = false)
                },
                getTasks(){
                    this.loading = true
                    fetch('/getTasks').then(res=>res.json()).then(msg=>{
                        this.rows = [];
                        for(let k in msg){
                            let time = new Date(Number.parseInt(k))
                            this.rows.push({tm:`${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`,schedule:msg[k],stamp:k})
                            this.rows.sort((x,y)=>x.stamp - y.stamp)
                        }
                    }).catch(err=>alert(err)).finally(()=>this.loading = false)
                }
            },
            mounted() {
                this.getTasks();
            },
            template: `<div v-loading="loading">
        <el-button type="primary" size="default" id="task" @click="getTasks()">刷新计划任务列表</el-button>
        <el-table :data="rows">
            <el-table-column v-for="col in columns"
                :prop="col.id"
                :key="col.id"
                :label="col.label"
                :width="col.width">
            </el-table-column>
            <el-table-column label="操作" width="100px">
                <template v-slot="{row,$index}">
                    <el-button type="primary" size="mini" @click="delTask(row,$index)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
    </div>`
        })
        var ws = new WebSocket('ws://' + location.host + '/state?token=' + [[${ session.id }]])
        new Vue({
            el: '#app',
            methods: {
                logoff() {
                    fetch('/logoff').then(res => {
                        location.pathname = '/'
                    })
                }
                [# th:if="${ismgr}"]
                ,initdb(){
                    if(confirm('危险操作!!! 确定要初始化数据库吗???')){
                        fetch('/mgr/initdb',{method:'post'}).then(res=>location.reload())
                    }
                },
                backupDb(){
                    var a = document.createElement('a')
                    var body = document.getElementsByTagName('body')[0]
                    a.href = '/mgr/backupdb/cfg.bak'
                    a.download = 'cfg.bak'
                    a.style.display = 'none'
                    body.appendChild(a)
                    a.click()
                    body.removeChild(a)
                },
                restoreDb(){
                    if(this.restoreClicked){
                        if(!this.fl) return alert('请选择文件!!!')
                        var fd = new FormData()
                        fd.append('file',this.fl)
                        fetch('/mgr/restore',{method:'post',body:fd}).then(res=>res.text())
                        .then(msg=>{
                            if(msg === 'ok'){
                                this.restoreClicked = false
                            }else{
                                alert(msg);
                            }
                            this.fl = null
                        })
                    }else{
                        this.restoreClicked = true
                    }
                },
                filechange(e){
                    e.target.files[0].slice(0,15).text().then(txt=>{
                        fetch('/mgr/validatebak',{method:'post',headers:formheader,body:'head='+txt})
                        .then(res=>res.text()).then(txt=>{
                            if(txt) {
                                this.fl = e.target.files[0]
                            }else{
                                alert('不是有效的备份文件')
                                e.target.value = null
                            }
                        })
                    })
                }
                [/]
            }
            [# th:if="${ismgr}"]
            ,data: {
                currenttab: 'net',
                tabs: [{
                    id: 1,
                    comp: 'net',
                    txt: '控制网络'
                }, {
                    id: 2,
                    comp: 'users',
                    txt: '管理用户'
                }, {
                    id: 3,
                    comp: 'room',
                    txt: '管理接口'
                }, {
                    id: 4,
                    comp: 'args',
                    txt: '管理参数'
                }, {
                    id: 5,
                    comp: 'log',
                    txt: '操作记录'
                }],
                restoreClicked: false,
                fl: null
            }
            [/]
        })
    </script>

</body>

</html>